﻿using Bettzueche.RLGlue.RLTypes;

namespace Bettzueche.RLGlue.RLComponent {
    /// <summary>
    /// Das Lernexperiment führt über RL-Glue das Experiment aus und beobachtet es.
    /// <para>
    /// Alle Experimente <b>können</b> die Methoden von IExperiment implementieren und aufrufen.
    /// </para><para></para>
    /// <para>
    /// This is also the definition of an RLGlue 'engine'. The network codec is one such engine.
    /// If you want to create your own network codec, or a 'direct-compile' RL-Glue 
    /// engine (like the one in RL-Viz), you should implement this interface.
    /// (Copyright 2007 Brian Tanner
    ///  brian@tannerpages.com
    ///  http://brian.tannerpages.com)
    /// </para>
    /// </summary>
    /// <seealso cref="AnEnvironment"/>
    /// <seealso cref="AnAgent"/>
    /// <remarks>
    /// Normalerweise ist das Schreiben des Experimentprogramms der kürzeste und einfachste Teil beim Schreiben Ihres
    /// ersten Lern-Experiments. Das Experiment Programm hat kein Interface zu implementieren und umfasst vor Allem Anrufe
    /// bereits bestehender RL-Glue-Funktionen. Das Experiment hat vier Hauptaufgaben:<para>
    /// a) Starte das Experiment
    /// b) Spezifiziere die Sequenz der Agenten-Umwelt-Interaktionen (Schritte)
    /// c) Extraktion und Analyse experimenteller Daten
    /// d) Versuch beenden und aufräumen.</para><para>
    /// Das Experiment Programm kann nur die RL-Glue-Interface-Funktionen aufrufen. Es kann auf keine Agenten oder Umwelt
    /// Funktionen direkt zugreifen.</para>
    /// </remarks>
    public interface IExperiment : IEnvironment, IAgent {

        #region Properties

        /// <summary>
        /// Gets number of steps in the current (or last) episode.
        /// </summary>
        /// <remarks>
        /// int RL_num_steps();
        /// </remarks>
        int StepCount { get; }

        /// <summary>
        /// Gets total reward of current episode, last episode respectively.
        /// </summary>
        double Return { get; }

        /// <summary>
        /// Gets the episode count.
        /// </summary>
        int Episodes { get; }

        #endregion

        /// <summary>
        /// Initialisierung der Umwelt, des Agenten und interner Datenstrukturen.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// String RL_init()</remarks>
        string Init();

        //ITaskSpec EnvInit(); // IEnvironment

        //void Agent_init(String task_spec); // IAgent



        /// <summary>
        /// Starts experiment (starts environment and agent).
        /// </summary>
        /// <param name="observation">out Parameter of initial state.</param>
        /// <param name="action">out Parameter of first action to perform.</param>
        /// <remarks>
        /// Observation_action RL_start()
        /// </remarks>
        void Start(out GeneralState observation, out GeneralAction action);

        //Observation RL_env_start()
        //GeneralState EnvStart(); // IEnvironment

        //Action RL_agent_start(Observation theObservation);
        //GeneralAction Agent_start(GeneralState observation); // IAgent



        /// <summary>
        /// Performs a learning step (env_step and agent_step).
        /// </summary>
        /// <param name="reward">out Parameter Reward of this step</param>
        /// <param name="nextObservation">out Parameter next State</param>
        /// <param name="nextAction">out paremeter of next action to perform.</param>
        /// <returns>
        /// true, if the agent reaches a terminal state. false otherwise
        /// </returns>
        /// <remarks>
        /// Beispielhafte (typische) Anwendung und Implemtierung:<para>
        /// Die RL-step Methode ruft <see cref="AnEnvironment.Env_step">Env_step</see> mit der jüngsten Agentenaktion 
        /// (zu Beginn die von Agent_start) auf.
        /// Es wird die neue Beobachtung (bzw. Folgezustand), die Belohnung und das Terminierungsflag zurückgegeben.
        /// Wenn das Flag nicht gesetzt ist, wird <see cref="AnAgent.Agent_step">Agent_step</see> mit der Belohnung und 
        /// Beobachtung als Argumente aufgerufen. Die Aktion, die Agent_step liefert, wird von RL-Glue bis zum nächsten Aufruf 
        /// von RL_step gespeichert.
        /// Wenn das Flag gesetzt ist, wird <see cref="AnAgent.Agent_end">Agent_end</see> mit dem Reward aufgerufen.
        /// Dieser Prozess wird fortgesetzt, bis entweder das Flag gesetzt oder bspw. 100 Schritte ausgeführt wurden.
        /// </para>
        /// <code>
        /// 1. RL_init()
        /// 2. RL_start()
        /// 3. steps=0
        /// 4. terminal=false 
        /// 5. while steps &lt; 100 and not terminal
        /// 6.     terminal,reward,observation,action = RL_step()
        /// 7.     steps=steps+1
        /// 8. RL_cleanup()
        /// </code>
        /// </remarks>
        bool Step(out double reward, out GeneralState nextObservation, out GeneralAction nextAction);

        //Reward_observation_terminal RL_env_step(Action theAction);
        //bool EnvStep(GeneralAction action, out double outReward, out GeneralState outObservation); // IEnvironment

        //Action RL_agent_step(double theReward, Observation theObservation);
        //GeneralAction Agent_step(double reward, GeneralState observation); // IAgent



        /// <summary>
        /// Release (dispose) resources after an experiment.
        /// </summary>
        /// <remarks>
        /// void RL_cleanup();
        /// </remarks>
        void CleanUp();

        //void EnvCleanup(); // IEnvironment

        //void Agent_cleanup(); // IAgent



        //String RL_env_message(String message);
        //string EnvMessage(string message); // IEnvironment

        //String RL_agent_message(String message);
        //String Agent_message(String message);



        /// <summary>
        /// Performs the specified number of <see cref="Step">RL_steps</see>.
        /// </summary>
        /// <param name="num_steps">Number (max) of Steps. Also 0, go without step limit to a final state.</param>
        /// <returns>exit status</returns>
        /// <remarks>
        /// int RL_episode(int numSteps);
        /// </remarks>
        int RunEpisode(int num_steps);



        //void RL_agent_end(double theReward);
        //void Agent_end(double reward); // IAgent

    }
}
